package com.share.mongo;

import com.mongodb.ConnectionString;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Accumulators;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Projections;
import com.mongodb.client.model.Sorts;
import org.bson.Document;
import org.bson.conversions.Bson;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.mongodb.client.model.Aggregates.*;

/**
 * @author caifan
 * @created 2021/3/26
 * description:
 */
public class MongoMain {
    public static void main(String[] args) {
        MongoClient mongoClient = MongoClients.create(new ConnectionString("mongodb://10.168.6.201:27017"));
        MongoDatabase mongo = mongoClient.getDatabase("mongo");
//        MongoCollection<Document> employees = mongo.getCollection("employees");
//        insert(employees);
//        query(employees);
//        queryOr(employees);
//        update(employees);
//        delete(employees);

        MongoCollection<Document> countries = mongo.getCollection("countries");
        matchAndCount(countries);
        groupAndSort(countries);

        sortLimitOut(countries, mongo);

        projectGroupMatch(countries);
    }

    /**
     *  插入数据
     * @param employees
     */
    public static void insert(MongoCollection<Document> employees) {
        Document employee = new Document()
                .append("first_name", "Joe")
                .append("last_name", "Smith")
                .append("title", "Java Developer")
                .append("years_of_service", 3)
                .append("skills", Arrays.asList("java", "spring", "mongodb"))
                .append("manager", new Document()
                        .append("first_name", "Sally")
                        .append("last_name", "Johanson"));
        employees.insertOne(employee);
    }

    /**
     * 简单条件查询
     * @param employees
     */
    public static void query(MongoCollection<Document> employees) {
        Document query = new Document("last_name", "Smith");
        List results = new ArrayList<>();
        employees.find(query).into(results);
        results.stream().forEach(System.out::println);

    }

    /**
     * or 条件查询
     * 对应命令
     * {
     *   "$or": [
     *     { "first_name": "Joe" },
     *     { "last_name":"Smith" }
     *   ]
     * }
     * @param employees
     */
    public static void queryOr(MongoCollection<Document> employees) {
        Document query =
                new Document("$or", Arrays.asList(
                        new Document("last_name", "Smith"),
                        new Document("first_name", "Joe")));
        List results = new ArrayList<>();
        employees.find(query).into(results);
        results.stream().forEach(System.out::println);
    }

    /**
     *
     * Update queries are a little different in MongoDB because they require two documents:     *
     *     The filter criteria to find one or more documents
     *     An update document specifying which fields to modify
     *  eg:我们想要为每个有spring技能的employee添加 security，第一个document将查找所有包含spring的employees，然后再添加security技能
     * {
     *   "skills": {
     *     $elemMatch:  {
     *       "$eq": "spring"
     *     }
     *   }
     * }
     *
     * {
     *   "$push": {
     *     "skills": "security"
     *   }
     * }
     * @param employees
     */
    public static void update(MongoCollection<Document> employees) {
        Document query = new Document("skills",
                new Document("$elemMatch",
                        new Document("$eq", "spring")));
        Document update = new Document("$push",
                new Document("skills", "security"));
        employees.updateMany(query, update);
    }

    /**
     * 删除
     *
     * {
     *   "years_of_service" : {
     *     "$lt" : 0
     *   }
     * }
     * @param employees
     */
    public static void delete(MongoCollection<Document> employees) {
        Document query = new Document(
                "years_of_service",
                new Document("$lt", 0));
        employees.deleteMany(query);
    }

    /**
     * match count
     * @param countries
     */
    public static void matchAndCount(MongoCollection<Document> countries) {
        Document englishSpeakingCountries = countries.aggregate(Arrays.asList(
                match(Filters.eq("languages.name", "English")),
                count())).first();
        System.out.println(englishSpeakingCountries.get("count"));

    }

    /**
     * 分组 排序
     * @param countries
     */
    public static void groupAndSort(MongoCollection<Document> countries) {
        Document maxCountriedRegion = countries.aggregate(Arrays.asList(
                group("$region", Accumulators.sum("tally", 1)),
                sort(Sorts.descending("tally")))).first();
        System.out.println(maxCountriedRegion.containsValue("Africa"));
    }

    public static void sortLimitOut(MongoCollection<Document> countries, MongoDatabase mongo) {
        countries.aggregate(Arrays.asList(
                sort(Sorts.descending("area")),
                limit(7),
                out("largest_seven"))).toCollection();

        MongoCollection<Document> largestSeven = mongo.getCollection("largest_seven");

        Document usa = largestSeven.find(Filters.eq("alpha3Code", "USA")).first();

        System.out.println(usa);
    }

    public static void projectGroupMatch(MongoCollection<Document> countries) {
        Bson borderingCountriesCollection = project(Projections.fields(Projections.excludeId(),
                Projections.include("name"), Projections.computed("borderingCountries",
                        Projections.computed("$size", "$borders"))));

        int maxValue = countries.aggregate(Arrays.asList(borderingCountriesCollection,
                group(null, Accumulators.max("max", "$borderingCountries"))))
                .first().getInteger("max");

        System.out.println("maxvalue:" + maxValue);

        Document maxNeighboredCountry = countries.aggregate(Arrays.asList(borderingCountriesCollection,
                match(Filters.eq("borderingCountries", maxValue)))).first();
        System.out.println(maxNeighboredCountry.containsValue("China"));
    }
}
